//: C25:BeeAndFlowers.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Demonstration of "visitor" pattern
#include "../purge.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;

class Gladiolus;
class Renuculus;
class Chrysanthemum;

class Visitor
{
public:
    virtual void visit(Gladiolus* f) = 0;
    virtual void visit(Renuculus* f) = 0;
    virtual void visit(Chrysanthemum* f) = 0;
    virtual ~Visitor() {}
};

class Flower
{
public:
    virtual void accept(Visitor&) = 0;
    virtual ~Flower() {}
};

class Gladiolus : public Flower
{
public:
    virtual void accept(Visitor& v) {
        v.visit(this);
    }
};

class Renuculus : public Flower
{
public:
    virtual void accept(Visitor& v) {
        v.visit(this);
    }
};

class Chrysanthemum : public Flower
{
public:
    virtual void accept(Visitor& v) {
        v.visit(this);
    }
};

// Add the ability to produce a string:
class StringVal : public Visitor
{
    string s;
public:
    operator const string&() {
        return s;
    }
    virtual void visit(Gladiolus*) {
        s = "Gladiolus";
    }
    virtual void visit(Renuculus*) {
        s = "Renuculus";
    }
    virtual void visit(Chrysanthemum*) {
        s = "Chrysanthemum";
    }
};

// Add the ability to do "Bee" activities:
class Bee : public Visitor
{
public:
    virtual void visit(Gladiolus*) {
        cout << "Bee and Gladiolus\n";
    }
    virtual void visit(Renuculus*) {
        cout << "Bee and Renuculus\n";
    }
    virtual void visit(Chrysanthemum*) {
        cout << "Bee and Chrysanthemum\n";
    }
};

struct FlowerGen {
    FlowerGen() {
        srand(time(0));
    }
    Flower* operator()() {
        switch (rand() % 3) {
        default:
        case 0:
            return new Gladiolus();
        case 1:
            return new Renuculus();
        case 2:
            return new Chrysanthemum();
        }
    }
};

int main()
{
    vector<Flower*> v(10);
    generate(v.begin(), v.end(), FlowerGen());
    vector<Flower*>::iterator it;
    // It's almost as if I added a virtual function
    // to produce a Flower string representation:
    StringVal sval;
    for (it = v.begin(); it != v.end(); it++) {
        (*it)->accept(sval);
        cout << string(sval) << endl;
    }
    // Perform "Bee" operation on all Flowers:
    Bee bee;
    for (it = v.begin(); it != v.end(); it++)
        (*it)->accept(bee);
    purge(v);
} ///:~
